use parking_lot::Mutex;
use std::rc::Rc;
use std::sync::Arc;
use winit::{
    application::ApplicationHandler,
    event::WindowEvent,
    event_loop::{ActiveEventLoop, EventLoop},
    window::{Window, WindowId}
};

struct WGPUApp {
    #[allow(unused)]
    window: Arc<Window>,
}

impl WGPUApp {
    async fn new(window: Arc<Window>) -> Self {
        #[cfg(target_arch = "wasm32")]
        {
            use winit::platform::web::WindowExtWebSys;

            let canvas = window.canvas().unwrap();

            web_sys::window().and_then(|win| win.document()).map(|doc| {
                let _ = canvas.set_attribute("id", "winit-canvas");
                match doc.get_element_by_id("wgpu-app-container") {
                    Some(dst) => {
                        let _ = dst.append_child(canvas.as_ref());
                    }
                    None => {
                        let container = doc.create_element("div").unwrap();
                        let _ = container.set_attribute("id", "wgpu-app-container");
                        let _ = container.append_child(canvas.as_ref());

                        doc.body().map(|body| body.append_child(container.as_ref()));
                    }
                };
            }).expect("failed to add canvas to web");

            // canvas focus catch
            // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
            canvas.set_tab_index(0);

            let style = canvas.style();
            style.set_property("outline", "none").unwrap();
            canvas.focus().expect("failed to get focus");
        }
        Self { window }
    }
}

#[derive(Default)]
struct WGPUAppHandler {
    app: Rc<Mutex<Option<WGPUApp>>>,
}

impl ApplicationHandler for WGPUAppHandler {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        // resume event
        if self.app.as_ref().lock().is_some() {
            return;
        }

        let win_attributes = Window::default_attributes().with_title("tutorial1-window");
        let win = Arc::new(event_loop.create_window(win_attributes).unwrap());

        cfg_if::cfg_if! {
            if #[cfg(target_arch = "wasm32")] {
                let app = self.app.clone();
                wasm_bindgen_futures::spawn_local(async move {
                    let wgpu_app = WGPUApp::new(win).await;
                    let mut app = app.lock();
                    *app = Some(wgpu_app);
                });
            } else {
                let wgpu_app = pollster::block_on(WGPUApp::new(win));
                self.app.lock().replace(wgpu_app);
            }
        }
    }

    fn suspended(&mut self, event_loop: &ActiveEventLoop) {
        // suspend event
    }

    fn window_event(
            &mut self,
            event_loop: &ActiveEventLoop,
            window_id: WindowId,
            event: WindowEvent,
        ) {
        // window event
        match event {
           WindowEvent::CloseRequested => {
            event_loop.exit();
           } 
           WindowEvent::Resized(_size) => {
           }
           WindowEvent::KeyboardInput {..} => {
           }
           WindowEvent::RedrawRequested => {
           }
           _ => (),
        }
    }
}

fn main() -> Result<(), impl std::error::Error> {
    utils::init_logger();

    let events_loop = EventLoop::new().unwrap();
    let mut app = WGPUAppHandler::default();
    events_loop.run_app(&mut app)
}